/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "subcmd.h"

#define MAXLISTSIZE 256

static json *clusterinfo_2json(struct QuickPoolClusterInfo *);
static void listcluster_usage(const char *);

static void clusterinfo_usage(const char *);

/**
 * @brief       qp cluster list handler, to query cluster information.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 *
 * @retval      0             #1: succeed
 * @retval      1             #2: input option is wrong or kind of input argument not specified
 * @retval      3             #3: failed to query cluster information
 * @retval      4             #4: not enough memory to print json string
 *
 */
int
cmd_list_clusters(int argc, char **argv)
{
    while (TRUE) {
        int option_index = 0;
        static struct option long_options[] = {
            {"help", no_argument, 0, 'h'},
            {NULL, 0, 0, 0}
        };

        int opt;
        opt = getopt_long(argc, argv, "h", long_options, &option_index);
        if (-1 == opt) {
            break;
        }

        switch (opt) {
	case 'h':
            listcluster_usage(argv[0]);
            exit(0);
        case '?':
        default:
            listcluster_usage(argv[0]);
            exit(1);
        }
    }

    // if not specify cluster name, set all to TRUE
    int all = FALSE;
    unsigned int numCluster;
    char **clusters=NULL, **clusterPoint=NULL;
    numCluster = get_argument_names(argc, argv, optind, &clusters, &all, "cluster");
    if (!all) {
        clusterPoint = clusters;
    }

    if (MAXLISTSIZE <= numCluster) {
        fprintf(stderr, "Specified too many cluster names (maximum %d).\n", MAXLISTSIZE);
        FREEUP(clusters);
        exit(1);
    }

    int options = 0;
    struct request clusterRequest;
    memset(&clusterRequest, 0, sizeof(struct request));

    unsigned int size, n=0;
    clusterRequest.keyvalues = (struct keyvalue *)calloc(3, sizeof(struct keyvalue));
    if (NULL == clusterRequest.keyvalues) {
        fprintf(stderr, "calloc(%ld) failed due to %m.\n", 3*sizeof(struct keyvalue));
        exit(4);
    }

    clusterRequest.keyvalues[n].key = REQUEST_KEY_OPTIONS;
    clusterRequest.keyvalues[n].value = (char *)calloc(12, sizeof(char));
    if (NULL == clusterRequest.keyvalues[n].value) {
        fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
        exit(4);
    }
    snprintf(clusterRequest.keyvalues[n].value, 12, "%d", options);
    n++;

    unsigned int i;
    if (0 < numCluster && NULL != clusterPoint) {

        // to query specific clusters.
        clusterRequest.keyvalues[n].key = REQUEST_KEY_NUMBER;
        clusterRequest.keyvalues[n].value = (char *)calloc(12, sizeof(char));
        if (NULL == clusterRequest.keyvalues[n].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", 12);
            exit(4);
        }
        snprintf(clusterRequest.keyvalues[n].value, 12, "%d", numCluster);
        n++;

        clusterRequest.keyvalues[n].key = REQUEST_KEY_NAMES;

        size = 0;
        for (i = 0; i < numCluster; i++) {
            size += strlen(clusterPoint[i]) + 1;
        }
        clusterRequest.keyvalues[n].value = (char *)calloc(size, sizeof(char));
        if (NULL == clusterRequest.keyvalues[n].value) {
            fprintf(stderr, "calloc(%d) failed due to %m.\n", size);
            exit(4);
        }

        for (i = 0; i < numCluster; i++) {

            if (i == 0) {
                strcpy(clusterRequest.keyvalues[n].value, clusterPoint[i]);
            } else {
                strcat(clusterRequest.keyvalues[n].value, " ");
                strcat(clusterRequest.keyvalues[n].value, clusterPoint[i]);
            }
        }
        n++;
    }
    clusterRequest.number = n;
    FREEUP(clusters);

    struct QuickPoolClusterReply *clusterReply;
    clusterReply = qp_clusterinfo(&clusterRequest, NULL);
    if (quickpool_errno != 0 || NULL == clusterReply) {

        if (NULL != clusterReply) {
            // JM replied error message.
            fprintf(stderr, "%s\n", clusterReply->error_message);

            gf_xdr_free(libxdr_clusterinfo_reply, clusterReply);
            FREEUP(clusterReply);
        } else {
            fprintf(stderr, "Fail to query cluster information due to above error.\n");
        }

        gf_xdr_free(gf_xdr_request, &clusterRequest);

        exit(3);
    } 

    gf_xdr_free(gf_xdr_request, &clusterRequest);

    // print out cluster information in json format.
    json *jsonClusterList = json_CreateObject();
    for (i=0; i<clusterReply->num_clusters; i++) {

        json *jsonCluster = clusterinfo_2json(&(clusterReply->clusters[i]));
        json_AddItemToObject(jsonClusterList, clusterReply->clusters[i].cluster_name, jsonCluster);
    }

    char *strJson;
    strJson = json_Print(jsonClusterList);
    if (NULL != strJson) {
        fprintf(stdout, "%s\n", strJson);
        FREEUP(strJson);
    } else {

        perror("malloc");
        json_Delete(jsonClusterList);
        gf_xdr_free(libxdr_clusterinfo_reply, clusterReply);
        FREEUP(clusterReply);

        exit(4);
    }
    json_Delete(jsonClusterList);

    gf_xdr_free(libxdr_clusterinfo_reply, clusterReply);
    FREEUP(clusterReply);

    exit(0);
} // end function cmd_list_clusters

/**
 * @brief       qp cluster info handler, to query cluster name and master name.
 *
 * @param[in]   argc          #1: number of input argument
 * @param[in]   argv          #2: string array of the input argument
 *
 * @retval      0             #1: succeed
 * @retval      1             #2: input option is wrong or kind of input argument not specified
 * @retval      3             #3: failed to query cluster name and master name
 * @retval      4             #4: not enough memory to print json string
 *
 */
int
cmd_cluster_info(int argc, char **argv)
{
    while (TRUE) {
        int option_index = 0;
        static struct option long_options[] = {
            {"help", no_argument, 0, 'h'},
            {NULL, 0, 0, 0}
        };

        int opt;
        opt = getopt_long(argc, argv, "h", long_options, &option_index);
        if (-1 == opt) {
            break;
        }

        switch (opt) {
        case 'h':
            clusterinfo_usage(argv[0]);
            exit(0);
        case '?':
        default:
            clusterinfo_usage(argv[0]);
            exit(1);
        }
    }

    char *masterName;
    masterName = qp_getmastername(); // call JM to query my master name.
    if (NULL == masterName) {
        exit(3);
    }

    char *clusterName;
    clusterName = qp_getclustername(); // call JM to query my cluster name.
    if (NULL == clusterName) {
        exit(3);
    }
    
    json *jsonCluster = json_CreateObject();
    json_AddStringToObject(jsonCluster, "CLUSTER_NAME", clusterName);
    json_AddStringToObject(jsonCluster, "MASTER_NODE", masterName);

    char *strJson;
    strJson = json_Print(jsonCluster);
    if (NULL != strJson) {
        fprintf(stdout, "%s\n", strJson);
        FREEUP(strJson);
    } else {

        perror("malloc");
        json_Delete(jsonCluster);
        FREEUP(clusterName);

        exit(4);
    }
    json_Delete(jsonCluster);

    FREEUP(clusterName);

    exit(0);
} // end function cmd_cluster_info

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

/**
 * @brief       transform cluster structure to json.
 *
 * @param[in]   cluster       #1: cluster to be tranformed
 *
 * @retval      not NULL      #1: transformed json object
 *
 */
static json *
clusterinfo_2json(struct QuickPoolClusterInfo *cluster)
{
    json *jsonCluster = json_CreateObject();

    s2j_json_set_basic_element(jsonCluster, cluster, string, "CLUSTER_NAME", cluster_name);

    if (1 == cluster->status) {
        json_AddStringToObject(jsonCluster, "STATUS", "ok");
    } else {
        json_AddStringToObject(jsonCluster, "STATUS", "unavail");
    }

    s2j_json_set_basic_element(jsonCluster, cluster, string, "MASTER_HOST", master_name);

    s2j_json_set_basic_element(jsonCluster, cluster, int, "SERVERS", num_servers);

    unsigned int numHost = cluster->num_clients + cluster->num_servers;
    json_AddNumberToObject(jsonCluster, "HOSTS", numHost);

    s2j_json_set_array_element(jsonCluster, cluster, string, "ADMINS", admins, cluster->num_admins);
    s2j_json_set_array_element(jsonCluster, cluster, string, "RESOURCES",
                               resource_names, cluster->num_resources);
    s2j_json_set_array_element(jsonCluster, cluster, string, "HOST_TYPES",
                               host_types, cluster->num_types);
    s2j_json_set_array_element(jsonCluster, cluster, string, "HOST_MODELS",
                               host_models, cluster->num_models);

    return jsonCluster;
} // end function clusterinfo_2json

static void
listcluster_usage(const char *cmd)
{
    fprintf(stdout, "qp cluster %s usage: [-h, --help] [cluster_name ...]\n", cmd);

    return;
}

static void
clusterinfo_usage(const char *cmd)
{
    fprintf(stdout, "qp cluster %s: [-h, --help] \n", cmd);

    return;
}
